defmodule SockWorkstation.CollHelperGen do
  use GenServer

  @name __MODULE__
  # 示例分隔符（可自定义）
  @separator <<0xDE, 0xAD, 0xBE, 0xEF>>

  # 客户端API
  def start_link(_opts) do
    GenServer.start_link(@name, [], name: @name)
  end

  def add_frame(frame) when is_binary(frame) do
    GenServer.cast(@name, {:add_frame, frame})
  end

  def get_state() do
    GenServer.call(@name, :get_state)
  end

  def clear_state() do
    GenServer.cast(@name, :clear_state)
  end

  def get_frames() do
    GenServer.call(@name, :get_frames)
  end

  def read_pcm_addtodb(filename, db, docid, attachment_name) do
    read_file_add_frame(filename)
    Process.sleep(120)
    append_to_couchdb(db, docid, attachment_name)
  end

  def save_to_file(filename \\ "opus.bin") do
    GenServer.call(@name, {:save_to_file, filename})
  end

  def save_to_couchdb(filename) do
    GenServer.cast(@name, {:save_to_couchdb, filename})
  end

  # def save_to_couchdb(db, sessid) do
  #   GenServer.cast(@name, {:save_to_couchdb, db, sessid})
  # end

  def save_to_couchdb(db, docid, attachment_name) do
    GenServer.cast(@name, {:save_to_couchdb, db, docid, attachment_name})
  end

  def append_to_couchdb(db, docid, attachment_name) do
    GenServer.cast(@name, {:append_to_couchdb, db, docid, attachment_name})
  end

  def read_file_add_frame(filename) do
    File.exists?(filename) || raise "File not found: #{filename}"
    stream = File.stream!(filename, [:binary], 1920)

    Enum.each(stream, fn chunk ->
      if byte_size(chunk) == 1920 do
        {:ok, opus_data} = SockWorkstation.CodecNifGen.encode_pcm("1150", chunk)

        add_frame(opus_data)
      end
    end)
  end

  # 服务器实现
  @impl true
  def init(_opts) do
    # {io_list, total_size}
    state = {[], 0}
    {:ok, state}
  end

  @impl true
  def handle_call(:get_state, _from, state) do
    {:reply, state, state}
  end

  def handle_call(:get_frames, _from, {io_list, _} = state) do
    # 反转列表以保持原始顺序，并合并为二进制
    binary = io_list |> Enum.reverse() |> Enum.intersperse(@separator) |> IO.iodata_to_binary()
    {:reply, binary, state}
  end

  def handle_call({:save_to_file, filename}, _from, {io_list, _} = _state) do
    # {frames, _} = state
    # 将帧列表反转以保持原始顺序
    # reversed_frames = Enum.reverse(frames)

    binary =
      io_list
      |> Enum.reverse()
      |> Enum.intersperse(@separator)
      |> IO.iodata_to_binary()

    File.write!(filename, binary)
    state = {[], 0}
    {:reply, :ok, state}
    # binary =
    #   Enum.reduce(frames, <<>>, fn frame, acc ->
    #     acc <> <<byte_size(frame)::32>> <> frame
    #   end)

    # 方法1: 直接写入文件(简单)
    # File.write!(filename, binary)

    # 方法2: 使用IO列表高效写入
    # result =
    #   reversed_frames
    #   |> IO.iodata_to_binary()
    #   |> then(&File.write(filename, &1))

    # {:reply, result, state}
  end

  @impl true
  def handle_cast(:clear_state, _state) do
    {:noreply, {[], 0}}
  end

  def handle_cast({:add_frame, frame}, {io_list, total_size}) do
    new_size = total_size + byte_size(frame)
    {:noreply, {[frame | io_list], new_size}}
  end

  def handle_cast({:save_to_couchdb, filename}, {frames, _} = _state) do
    binary =
      frames
      |> Enum.reverse()
      |> Enum.slice(6..-3//1)
      |> Enum.intersperse(@separator)

    attachment = %{
      doc_id: "song_#{filename}",
      name: filename,
      # binary,
      data: binary,
      content_type: "application/octet-stream"
    }

    with {:ok, _} <- SockWorkstation.PanCouchbeamClient.save_attachment("aod_opus_1", attachment) do
      {:noreply, {[], 0}}
    end
  end

  # def handle_cast({:save_to_couchdb, db, session_id}, {frames, _} = _state) do
  #   binary =
  #     frames
  #     |> Enum.reverse()
  #     |> Enum.intersperse(@separator)

  #   attachment = %{
  #     doc_id: session_id,
  #     name: session_id,
  #     # binary,
  #     data: binary,
  #     content_type: "application/octet-stream"
  #   }

  #   case SockWorkstation.PanCouchbeamClient.save_attachment(db, attachment) do
  #     {:ok, _} ->
  #       {:noreply, {[], 0}}

  #     _ ->
  #       {:noreply, {[], 0}}
  #   end
  # end

  def handle_cast({:save_to_couchdb, db, docid, attachment_name}, {frames, _} = _state) do
    binary =
      frames
      |> Enum.reverse()
      |> Enum.slice(6..-3//1)
      |> Enum.intersperse(@separator)
      |> IO.iodata_to_binary()

    attachment = %{
      doc_id: docid,
      name: attachment_name,
      # binary,
      data: binary,
      content_type: "application/octet-stream"
    }

    case SockWorkstation.PanCouchbeamClient.save_attachment(db, attachment) do
      {:ok, _} ->
        {:noreply, {[], 0}}

      _ ->
        {:noreply, {[], 0}}
    end
  end

  def handle_cast({:append_to_couchdb, db, docid, attachment_name}, {frames, _} = _state) do
    binary =
      frames
      |> Enum.reverse()
      |> Enum.slice(6..-3//1)
      |> Enum.intersperse(@separator)
      # 确保最终合并为二进制
      |> IO.iodata_to_binary()

    attachment = %{
      # doc_id: docid,
      name: attachment_name,
      # binary,
      data: binary,
      content_type: "application/octet-stream"
    }

    case SockWorkstation.PanCouchbeamClient.update_attachment(db, docid, attachment) do
      {:ok, _} ->
        {:noreply, {[], 0}}

      _ ->
        {:noreply, {[], 0}}
    end
  end

  def read_frames_with_separator(binary) do
    # with {:ok, binary} <- File.read(filename) do
    # 使用二进制模式匹配分割
    frames = binary |> split_by_separator(@separator)
    {:ok, frames}
    # end
  end

  defp split_by_separator(binary, separator) do
    separator_size = byte_size(separator)

    case binary do
      # 以分隔符开头的情况
      <<^separator::binary-size(separator_size), rest::binary>> ->
        do_split(rest, [], separator)

      # 正常情况
      _ ->
        do_split(binary, [], separator)
    end
  end

  defp do_split(<<>>, acc, _), do: Enum.reverse(acc)

  defp do_split(binary, acc, separator) do
    case :binary.split(binary, separator) do
      [frame, rest] ->
        do_split(rest, [frame | acc], separator)

      [last_frame] ->
        Enum.reverse([last_frame | acc])
    end
  end

  # defp split_by_separator(binary, separator) do
  #   separator_size = byte_size(separator)

  #   binary
  #   |> Stream.unfold(fn
  #     <<>> -> nil
  #     rest ->
  #       case :binary.split(rest, separator) do
  #         [frame, remaining] -> {frame, remaining}
  #         [last_frame] -> {last_frame, <<>>}
  #       end
  #   end)
  #   |> Enum.to_list()
  # end
end
